Golang在生成汇编语句的时候,赋值是一句话,所以:COW Copy-On-Write是原子性的
先说一下我这边的一个简化场景吧,有一个定时任务定时从数据库获取数据,也就是对应实例代码中的getNewProject(),获取完数据后,会直接赋给变量projectMap(projectMap其实就是作为一个缓存来用的);还会有程序从projectMap获取对应的信息。
这个场景其实就是一个简单的写时复制。对于获取在赋值过程中,获取到旧值,也是允许的。
有个疑问点就是在赋值的这个操作是不是原子的呢?
比如示例代码中的第8行。
验证代码:
1 | package main |
在网上也找到一些资料,说是原子性的:
- 看看开源项目都是如何做的
- 看看一些对于Copy-On-Write的讨论
但终究还是自己探索一下的比较好对吧,哈哈。
要想看这个赋值操作是不是原子性的,那咱们看下汇编代码吧。
查看汇编,咱们分为两步:
- 编译golang代码为.o文件
- 反编译.o文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188PS D:\Code\Golang\github\jiankunking\cow-test> go tool compile -N -l main\main.go
PS D:\Code\Golang\github\jiankunking\cow-test> go tool objdump .\main.o
TEXT "".main(SB) gofile..D:/Code/Golang/github/jiankunking/cow-test/main/main.go
main.go:7 0x22f1 493b6610 CMPQ 0x10(R14), SP
main.go:7 0x22f5 763e JBE 0x2335
main.go:7 0x22f7 4883ec08 SUBQ $0x8, SP
main.go:7 0x22fb 48892c24 MOVQ BP, 0(SP)
main.go:7 0x22ff 488d2c24 LEAQ 0(SP), BP
main.go:8 0x2303 e800000000 CALL 0x2308 [1:5]R_CALL:"".getNewProject
main.go:8 0x2308 833d0000000000 CMPL $0x0, 0(IP) [2:6]R_PCREL:runtime.writeBarrier+-1
main.go:8 0x230f 6690 NOPW
main.go:8 0x2311 7402 JE 0x2315
main.go:8 0x2313 eb09 JMP 0x231e
main.go:8 0x2315 48890500000000 MOVQ AX, 0(IP) [3:7]R_PCREL:"".projectMap
main.go:8 0x231c eb0e JMP 0x232c
main.go:8 0x231e 488d3d00000000 LEAQ 0(IP), DI [3:7]R_PCREL:"".projectMap
main.go:8 0x2325 e800000000 CALL 0x232a [1:5]R_CALL:runtime.gcWriteBarrier<1>
main.go:8 0x232a eb00 JMP 0x232c
main.go:9 0x232c 488b2c24 MOVQ 0(SP), BP
main.go:9 0x2330 4883c408 ADDQ $0x8, SP
main.go:9 0x2334 c3 RET
main.go:7 0x2335 e800000000 CALL 0x233a [1:5]R_CALL:runtime.morestack_noctxt
main.go:7 0x233a ebb5 JMP "".main(SB)
TEXT "".getNewProject(SB) gofile..D:/Code/Golang/github/jiankunking/cow-test/main/main.go
main.go:11 0x233c 493b6610 CMPQ 0x10(R14), SP
main.go:11 0x2340 0f8600010000 JBE 0x2446
main.go:11 0x2346 4883ec58 SUBQ $0x58, SP
main.go:11 0x234a 48896c2450 MOVQ BP, 0x50(SP)
main.go:11 0x234f 488d6c2450 LEAQ 0x50(SP), BP
main.go:11 0x2354 48c744242000000000 MOVQ $0x0, 0x20(SP)
main.go:12 0x235d e800000000 CALL 0x2362 [1:5]R_CALL:runtime.makemap_small<1>
main.go:12 0x2362 4889442428 MOVQ AX, 0x28(SP)
main.go:14 0x2367 488d0500000000 LEAQ 0(IP), AX [3:7]R_PCREL:type."".Project
main.go:14 0x236e e800000000 CALL 0x2373 [1:5]R_CALL:runtime.newobject<1>
main.go:14 0x2373 4889442430 MOVQ AX, 0x30(SP)
main.go:15 0x2378 48c740080a000000 MOVQ $0xa, 0x8(AX)
main.go:15 0x2380 833d0000000000 CMPL $0x0, 0(IP) [2:6]R_PCREL:runtime.writeBarrier+-1
main.go:15 0x2387 7402 JE 0x238b
main.go:15 0x2389 eb0c JMP 0x2397
main.go:15 0x238b 488d1500000000 LEAQ 0(IP), DX [3:7]R_PCREL:go.string."project_id"
main.go:15 0x2392 488910 MOVQ DX, 0(AX)
main.go:15 0x2395 eb11 JMP 0x23a8
main.go:15 0x2397 4889c7 MOVQ AX, DI
main.go:15 0x239a 488d1500000000 LEAQ 0(IP), DX [3:7]R_PCREL:go.string."project_id"
main.go:15 0x23a1 e800000000 CALL 0x23a6 [1:5]R_CALL:runtime.gcWriteBarrierDX
main.go:15 0x23a6 eb00 JMP 0x23a8
main.go:16 0x23a8 488b542430 MOVQ 0x30(SP), DX
main.go:16 0x23ad 8402 TESTB AL, 0(DX)
main.go:16 0x23af 48c742180c000000 MOVQ $0xc, 0x18(DX)
main.go:16 0x23b7 488d7a10 LEAQ 0x10(DX), DI
main.go:16 0x23bb 833d0000000000 CMPL $0x0, 0(IP) [2:6]R_PCREL:runtime.writeBarrier+-1
main.go:16 0x23c2 7402 JE 0x23c6
main.go:16 0x23c4 eb0d JMP 0x23d3
main.go:16 0x23c6 488d3500000000 LEAQ 0(IP), SI [3:7]R_PCREL:go.string."project_name"
main.go:16 0x23cd 48897210 MOVQ SI, 0x10(DX)
main.go:16 0x23d1 eb10 JMP 0x23e3
main.go:16 0x23d3 488d1500000000 LEAQ 0(IP), DX [3:7]R_PCREL:go.string."project_name"
main.go:16 0x23da 6690 NOPW
main.go:16 0x23dc e800000000 CALL 0x23e1 [1:5]R_CALL:runtime.gcWriteBarrierDX
main.go:16 0x23e1 eb00 JMP 0x23e3
main.go:18 0x23e3 488b542430 MOVQ 0x30(SP), DX
main.go:18 0x23e8 8402 TESTB AL, 0(DX)
main.go:18 0x23ea 488b0a MOVQ 0(DX), CX
main.go:18 0x23ed 488b7a08 MOVQ 0x8(DX), DI
main.go:18 0x23f1 48894c2440 MOVQ CX, 0x40(SP)
main.go:18 0x23f6 48897c2448 MOVQ DI, 0x48(SP)
main.go:18 0x23fb 488b5c2428 MOVQ 0x28(SP), BX
main.go:18 0x2400 488d0500000000 LEAQ 0(IP), AX [3:7]R_PCREL:type.map[string]*"".Project
main.go:18 0x2407 e800000000 CALL 0x240c [1:5]R_CALL:runtime.mapassign_faststr<1>
main.go:18 0x240c 4889442438 MOVQ AX, 0x38(SP)
main.go:18 0x2411 8400 TESTB AL, 0(AX)
main.go:18 0x2413 488b542430 MOVQ 0x30(SP), DX
main.go:18 0x2418 833d0000000000 CMPL $0x0, 0(IP) [2:6]R_PCREL:runtime.writeBarrier+-1
main.go:18 0x241f 7402 JE 0x2423
main.go:18 0x2421 eb05 JMP 0x2428
main.go:18 0x2423 488910 MOVQ DX, 0(AX)
main.go:18 0x2426 eb0a JMP 0x2432
main.go:18 0x2428 4889c7 MOVQ AX, DI
main.go:18 0x242b e800000000 CALL 0x2430 [1:5]R_CALL:runtime.gcWriteBarrierDX
main.go:18 0x2430 eb00 JMP 0x2432
main.go:19 0x2432 488b442428 MOVQ 0x28(SP), AX
main.go:19 0x2437 4889442420 MOVQ AX, 0x20(SP)
main.go:19 0x243c 488b6c2450 MOVQ 0x50(SP), BP
main.go:19 0x2441 4883c458 ADDQ $0x58, SP
main.go:19 0x2445 c3 RET
main.go:11 0x2446 e800000000 CALL 0x244b [1:5]R_CALL:runtime.morestack_noctxt
main.go:11 0x244b e9ecfeffff JMP "".getNewProject(SB)
TEXT "".init(SB) gofile..D:/Code/Golang/github/jiankunking/cow-test/main/main.go
main.go:4 0x2450 493b6610 CMPQ 0x10(R14), SP
main.go:4 0x2454 763e JBE 0x2494
main.go:4 0x2456 4883ec08 SUBQ $0x8, SP
main.go:4 0x245a 48892c24 MOVQ BP, 0(SP)
main.go:4 0x245e 488d2c24 LEAQ 0(SP), BP
main.go:4 0x2462 e800000000 CALL 0x2467 [1:5]R_CALL:runtime.makemap_small<1>
main.go:4 0x2467 833d0000000000 CMPL $0x0, 0(IP) [2:6]R_PCREL:runtime.writeBarrier+-1
main.go:4 0x246e 6690 NOPW
main.go:4 0x2470 7402 JE 0x2474
main.go:4 0x2472 eb09 JMP 0x247d
main.go:4 0x2474 48890500000000 MOVQ AX, 0(IP) [3:7]R_PCREL:"".projectMap
main.go:4 0x247b eb0e JMP 0x248b
main.go:4 0x247d 488d3d00000000 LEAQ 0(IP), DI [3:7]R_PCREL:"".projectMap
main.go:4 0x2484 e800000000 CALL 0x2489 [1:5]R_CALL:runtime.gcWriteBarrier<1>
main.go:4 0x2489 eb00 JMP 0x248b
main.go:4 0x248b 488b2c24 MOVQ 0(SP), BP
main.go:4 0x248f 4883c408 ADDQ $0x8, SP
main.go:4 0x2493 c3 RET
main.go:4 0x2494 e800000000 CALL 0x2499 [1:5]R_CALL:runtime.morestack_noctxt
main.go:4 0x2499 ebb5 JMP "".init(SB)
TEXT type..eq."".Project(SB) gofile..<autogenerated>
gofile..<autogenerated>:1 0x2713 493b6610 CMPQ 0x10(R14), SP
gofile..<autogenerated>:1 0x2717 0f8610010000 JBE 0x282d
gofile..<autogenerated>:1 0x271d 4883ec58 SUBQ $0x58, SP
gofile..<autogenerated>:1 0x2721 48896c2450 MOVQ BP, 0x50(SP)
gofile..<autogenerated>:1 0x2726 488d6c2450 LEAQ 0x50(SP), BP
gofile..<autogenerated>:1 0x272b 4889442460 MOVQ AX, 0x60(SP)
gofile..<autogenerated>:1 0x2730 48895c2468 MOVQ BX, 0x68(SP)
gofile..<autogenerated>:1 0x2735 c644241e00 MOVB $0x0, 0x1e(SP)
gofile..<autogenerated>:1 0x273a 488b542460 MOVQ 0x60(SP), DX
gofile..<autogenerated>:1 0x273f 488b5208 MOVQ 0x8(DX), DX
gofile..<autogenerated>:1 0x2743 4889542428 MOVQ DX, 0x28(SP)
gofile..<autogenerated>:1 0x2748 488b542468 MOVQ 0x68(SP), DX
gofile..<autogenerated>:1 0x274d 488b5208 MOVQ 0x8(DX), DX
gofile..<autogenerated>:1 0x2751 4889542420 MOVQ DX, 0x20(SP)
gofile..<autogenerated>:1 0x2756 4839542428 CMPQ DX, 0x28(SP)
gofile..<autogenerated>:1 0x275b 7405 JE 0x2762
gofile..<autogenerated>:1 0x275d e9b3000000 JMP 0x2815
gofile..<autogenerated>:1 0x2762 eb00 JMP 0x2764
gofile..<autogenerated>:1 0x2764 488b542460 MOVQ 0x60(SP), DX
gofile..<autogenerated>:1 0x2769 488b5218 MOVQ 0x18(DX), DX
gofile..<autogenerated>:1 0x276d 4889542420 MOVQ DX, 0x20(SP)
gofile..<autogenerated>:1 0x2772 488b542468 MOVQ 0x68(SP), DX
gofile..<autogenerated>:1 0x2777 488b5218 MOVQ 0x18(DX), DX
gofile..<autogenerated>:1 0x277b 4889542428 MOVQ DX, 0x28(SP)
gofile..<autogenerated>:1 0x2780 4839542420 CMPQ DX, 0x20(SP)
gofile..<autogenerated>:1 0x2785 7405 JE 0x278c
gofile..<autogenerated>:1 0x2787 e987000000 JMP 0x2813
gofile..<autogenerated>:1 0x278c eb00 JMP 0x278e
gofile..<autogenerated>:1 0x278e 488b542460 MOVQ 0x60(SP), DX
gofile..<autogenerated>:1 0x2793 488b5208 MOVQ 0x8(DX), DX
gofile..<autogenerated>:1 0x2797 4889542428 MOVQ DX, 0x28(SP)
gofile..<autogenerated>:1 0x279c 488b542460 MOVQ 0x60(SP), DX
gofile..<autogenerated>:1 0x27a1 488b12 MOVQ 0(DX), DX
gofile..<autogenerated>:1 0x27a4 4889542448 MOVQ DX, 0x48(SP)
gofile..<autogenerated>:1 0x27a9 488b542468 MOVQ 0x68(SP), DX
gofile..<autogenerated>:1 0x27ae 488b1a MOVQ 0(DX), BX
gofile..<autogenerated>:1 0x27b1 48895c2440 MOVQ BX, 0x40(SP)
gofile..<autogenerated>:1 0x27b6 488b4c2428 MOVQ 0x28(SP), CX
gofile..<autogenerated>:1 0x27bb 488b442448 MOVQ 0x48(SP), AX
gofile..<autogenerated>:1 0x27c0 e800000000 CALL 0x27c5 [1:5]R_CALL:runtime.memequal<1>
gofile..<autogenerated>:1 0x27c5 8844241f MOVB AL, 0x1f(SP)
gofile..<autogenerated>:1 0x27c9 84c0 TESTL AL, AL
gofile..<autogenerated>:1 0x27cb 7502 JNE 0x27cf
gofile..<autogenerated>:1 0x27cd eb41 JMP 0x2810
gofile..<autogenerated>:1 0x27cf eb00 JMP 0x27d1
gofile..<autogenerated>:1 0x27d1 488b542460 MOVQ 0x60(SP), DX
gofile..<autogenerated>:1 0x27d6 488b5218 MOVQ 0x18(DX), DX
gofile..<autogenerated>:1 0x27da 4889542428 MOVQ DX, 0x28(SP)
gofile..<autogenerated>:1 0x27df 488b542460 MOVQ 0x60(SP), DX
gofile..<autogenerated>:1 0x27e4 488b5210 MOVQ 0x10(DX), DX
gofile..<autogenerated>:1 0x27e8 4889542438 MOVQ DX, 0x38(SP)
gofile..<autogenerated>:1 0x27ed 488b542468 MOVQ 0x68(SP), DX
gofile..<autogenerated>:1 0x27f2 488b5a10 MOVQ 0x10(DX), BX
gofile..<autogenerated>:1 0x27f6 48895c2430 MOVQ BX, 0x30(SP)
gofile..<autogenerated>:1 0x27fb 488b4c2428 MOVQ 0x28(SP), CX
gofile..<autogenerated>:1 0x2800 488b442438 MOVQ 0x38(SP), AX
gofile..<autogenerated>:1 0x2805 e800000000 CALL 0x280a [1:5]R_CALL:runtime.memequal<1>
gofile..<autogenerated>:1 0x280a 8844241e MOVB AL, 0x1e(SP)
gofile..<autogenerated>:1 0x280e eb0e JMP 0x281e
gofile..<autogenerated>:1 0x2810 eb05 JMP 0x2817
gofile..<autogenerated>:1 0x2812 90 NOPL
gofile..<autogenerated>:1 0x2813 eb02 JMP 0x2817
gofile..<autogenerated>:1 0x2815 eb00 JMP 0x2817
gofile..<autogenerated>:1 0x2817 c644241e00 MOVB $0x0, 0x1e(SP)
gofile..<autogenerated>:1 0x281c eb00 JMP 0x281e
gofile..<autogenerated>:1 0x281e 0fb644241e MOVZX 0x1e(SP), AX
gofile..<autogenerated>:1 0x2823 488b6c2450 MOVQ 0x50(SP), BP
gofile..<autogenerated>:1 0x2828 4883c458 ADDQ $0x58, SP
gofile..<autogenerated>:1 0x282c c3 RET
gofile..<autogenerated>:1 0x282d 4889442408 MOVQ AX, 0x8(SP)
gofile..<autogenerated>:1 0x2832 48895c2410 MOVQ BX, 0x10(SP)
gofile..<autogenerated>:1 0x2837 e800000000 CALL 0x283c [1:5]R_CALL:runtime.morestack_noctxt
gofile..<autogenerated>:1 0x283c 488b442408 MOVQ 0x8(SP), AX
gofile..<autogenerated>:1 0x2841 488b5c2410 MOVQ 0x10(SP), BX
gofile..<autogenerated>:1 0x2846 e9c8feffff JMP type..eq."".Project(SB)
PS D:\Code\Golang\github\jiankunking\cow-test>
先从汇编代码中摘取出,第8行代码对应的汇编
1 | main.go:8 0x2303 e800000000 CALL 0x2308 [1:5]R_CALL:"".getNewProject |
从汇编代码中可以看出,赋值的核心在下面这几句:
1 | // 将AX寄存器中的值赋给0(IP) |
从汇编可以看到赋值是只有一步,但获取值及赋值是分开的,也就是说对于写时复制这种场景来说,直接赋值是没有问题的
基础知识补充:
MOVQ
movb(8位)、movw(16位)、movl(32位)、movq(64位)
寄存器寻址:
1
2movl %eax, %edx
eax -> edx
https://blog.csdn.net/luoyhang003/article/details/46786591/
TESTB
TEST指令的行为与AND指令一样,除了不改变目的寄存器的值。例如,testq %rax, %rax 用来检查 %rax 是负数、零、还是正数。
LEAQ vs MOVQ
https://stackoverflow.com/questions/1699748/what-is-the-difference-between-mov-and-lea
PCDATA
https://www.cnblogs.com/binHome/p/13034103.html
CMPL
1 | CMPL %eax,%ebx |
JMP
无条件转移指令
JMP 389无条件转至0x0185地址处(十进制389转换成十六进制0x0185)
CALL
调用函数
CALL runtime.printnl(SB)表示通过printnl函数的内存地址发起调用
伪计数器
FP: Frame pointer: arguments and locals.(指向当前栈帧)
PC: Program counter: jumps and branches.(指向指令地址)
SB: Static base pointer: global symbols.(指向全局符号表)
SP: Stack pointer: top of stack.(指向当前栈顶部)
注意: 栈是向下整长 golang的汇编是调用者维护参数返回值跟返回地址。所以FP的值小于参数跟返回值
本机环境
x86-64( 又称x64,即英文词64-bit extended,64位拓展 的简写)是x86架构的64位拓展,向后兼容于16位及32位的x86架构。x64于1999年由AMD设计,AMD首次公开64位集以扩展给x86,称为“AMD64”。
编译命令详解
1 | PS D:\Code\Golang\github\jiankunking\cow-test> go tool compile -help |
拓展阅读
https://xargin.com/plan9-assembly/
https://github.com/cch123/golang-notes/blob/master/assembly.md